/**
 * @file xf_template.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2023-10-23
 *
 * Copyright (c) 2023, CorAL. All rights reserved.
 *
 */

/* ==================== [Includes] ========================================== */

#if CONFIG_XF_FAL_ENABLE

#include "xf_utils.h"
#include "xf_fal.h"
#include "xf_init.h"

#include "drvs.h"

#include "port_flash_config.h"

/* ==================== [Defines] =========================================== */

#define TAG "port_fal"

/* ==================== [Typedefs] ========================================== */

/* ==================== [Static Prototypes] ================================= */

static xf_err_t safe_flash_page_erase(uint32_t offset, size_t size);
static xf_err_t safe_flash_byte_write(uint32_t offset, const uint8_t *data, uint32_t blen);
static xf_err_t safe_flash_byte_read(uint32_t offset, uint8_t *buff, uint32_t blen);

static xf_err_t internal_flash_init(void);
static xf_err_t internal_flash_deinit(void);
static xf_err_t internal_flash_read(size_t src_offset, void *dst, size_t size);
static xf_err_t internal_flash_write(size_t dst_offset, const void *src, size_t size);
static xf_err_t internal_flash_erase(size_t offset, size_t size);

/* ==================== [Static Variables] ================================== */

static const xf_fal_flash_dev_t sc_internal_flash_dev = {
    .name           = INTERNAL_FLASH_NAME,
    .addr           = INTERNAL_FLASH_START_ADDR,
    .len            = INTERNAL_FLASH_LEN,
    .sector_size    = SECTOR_SIZE,
    .page_size      = PAGE_SIZE,
    .io_size        = IO_SIZE,
    .ops.init       = internal_flash_init,
    .ops.deinit     = internal_flash_deinit,
    .ops.read       = internal_flash_read,
    .ops.write      = internal_flash_write,
    .ops.erase      = internal_flash_erase,
};

/* 
    TODO 默认分区表在这里。
 */
static const xf_fal_partition_t internal_flash_partition_table[] = {
    {"LoadInfo",    INTERNAL_FLASH_NAME,  0,                 1 * PAGE_SIZE   },
    {"trimVal",     INTERNAL_FLASH_NAME,  1 * PAGE_SIZE,    15 * PAGE_SIZE   },
    {"User",        INTERNAL_FLASH_NAME, 16 * PAGE_SIZE, (63 - 16 + 1) *PAGE_SIZE },
    /* 对应分区表中的 "User" */
    {USER_PART,     INTERNAL_FLASH_NAME, 16 * PAGE_SIZE, (63 - 16 + 1) *PAGE_SIZE },
    {"CODE_Region", INTERNAL_FLASH_NAME,  4 * SECTOR_SIZE, (63 - 4 + 1) *SECTOR_SIZE },
};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

/* ==================== [Static Functions] ================================== */

static int port_xf_fal(void)
{
    xf_fal_register_flash_device(&sc_internal_flash_dev);
    xf_fal_register_partition_table(
        internal_flash_partition_table,
        ARRAY_SIZE(internal_flash_partition_table));
    return XF_OK;
}
XF_INIT_EXPORT_PREV(port_xf_fal);

static xf_err_t internal_flash_init(void)
{
    return XF_OK;
}

static xf_err_t internal_flash_deinit(void)
{
    return XF_OK;
}

static xf_err_t internal_flash_read(size_t src_offset, void *dst, size_t size)
{
    xf_err_t xf_ret = XF_OK;
    /* 检查 src_offset 是否在 flash 范围内 */
    if (src_offset + size >= sc_internal_flash_dev.len) {
        XF_LOGE(TAG, "out of range");
        XF_LOGE(TAG, "offset:%d\tsize:%d\tflash_len:%d",
                (int)src_offset, (int)size, (int)sc_internal_flash_dev.len);
        return XF_FAIL;
    }
    xf_ret = safe_flash_byte_read(src_offset + sc_internal_flash_dev.addr, dst, size);
    return xf_ret;
}

static xf_err_t internal_flash_write(size_t dst_offset, const void *src, size_t size)
{
    xf_err_t xf_ret = XF_OK;
    /* 检查 dst_offset 是否在 flash 范围内 */
    if (dst_offset + size >= sc_internal_flash_dev.len) {
        XF_LOGE(TAG, "out of range");
        XF_LOGE(TAG, "offset:%d\tsize:%d\tflash_len:%d",
                (int)dst_offset, (int)size, (int)sc_internal_flash_dev.len);
        return XF_FAIL;
    }
    xf_ret = safe_flash_byte_write(dst_offset + sc_internal_flash_dev.addr, src, size);
    return xf_ret;
}

static xf_err_t internal_flash_erase(size_t offset, size_t size)
{
    xf_err_t xf_ret = XF_OK;
    if (offset + size >= sc_internal_flash_dev.len) {
        return XF_FAIL;
    }
    xf_ret = safe_flash_page_erase(offset + sc_internal_flash_dev.addr, size);
    return xf_ret;
}

static xf_err_t safe_flash_page_erase(uint32_t offset, size_t size)
{
    // Check if offset is aligned to PAGE_SIZE
    if (offset % PAGE_SIZE != 0) {
        XF_LOGE(TAG, "Offset must be aligned to PAGE_SIZE.");
        XF_LOGD(TAG, "Provided offset: 0x%x", (int)offset);
        return XF_FAIL;
    }

    // Check if size is aligned to PAGE_SIZE
    if (size % PAGE_SIZE != 0) {
        XF_LOGE(TAG, "Size must be a multiple of PAGE_SIZE.");
        XF_LOGD(TAG, "Provided size: %d", (int)size);
        return XF_FAIL;
    }

    // Calculate the number of pages to erase
    size_t num_pages = size / PAGE_SIZE;

    XF_LOGD(TAG, "Erasing flash starting at offset 0x%x for %d pages.", (int)offset, (int)num_pages);

    // Erase each page
    for (size_t i = 0; i < num_pages; i++) {
        uint32_t page_offset = offset + (i * PAGE_SIZE);
        XF_LOGD(TAG, "Erasing page at offset 0x%x.", (int)page_offset);
        flash_page_erase(page_offset);
    }

    return XF_OK;
}

static xf_err_t safe_flash_byte_write(uint32_t offset, const uint8_t *data, uint32_t blen)
{
    if (offset % 4 != 0) {
        XF_LOGE(TAG, "offset must be a multiple of 4.");
        XF_LOGE(TAG, "offset:%d", (int)offset);
        return XF_FAIL;
    }
    if (blen % 4 != 0) {
        XF_LOGE(TAG, "blen must be a multiple of 4.");
        XF_LOGE(TAG, "blen: %d", (int)blen);
        return XF_FAIL;
    }

    uint32_t remaining = blen;
    uint32_t current_offset = offset;
    const uint8_t *current_data = data; // 指向当前数据块的指针

    while (remaining > 0) {
        uint32_t page_start = ALIGN_DOWN(current_offset, PAGE_SIZE); // 当前页面的开始
        uint32_t chunk_start_offset = current_offset - page_start; // 当前页面内的偏移量
        uint32_t chunk_size = PAGE_SIZE - chunk_start_offset; // 可以写入当前页面的最大数据

        // 如果剩余数据少于整页，则调整块大小
        if (chunk_size > remaining) {
            chunk_size = remaining;
        }

        XF_LOGD(TAG, "page_start \t0x%x", (int)page_start);
        XF_LOGD(TAG, "chunk_start_offset \t0x%x", (int)chunk_start_offset);
        XF_LOGD(TAG, "chunk_size \t0x%x", (int)chunk_size);

        // 如果当前偏移量没有与 0x100 对齐或者数据跨多页
        if (chunk_start_offset != 0 || chunk_size < PAGE_SIZE) {
            XF_LOGD(TAG, "Offset not aligned to 0x100 or data spans multiple pages.");
            XF_LOGD(TAG, "Current offset: 0x%x", (int)current_offset);

            /*
                TODO: WARNING:
                此处使用了 xf_malloc, 内存太小，使用栈数组不一定安全
             */
            uint8_t *temp_buffer;
            temp_buffer = xf_malloc(PAGE_SIZE);
            if (temp_buffer == NULL) {
                XF_LOGE(TAG, "Failed to allocate temporary buffer.");
                return XF_FAIL;
            }
            memset(temp_buffer, 0xFF, PAGE_SIZE);

            flash_byte_read(page_start, temp_buffer, PAGE_SIZE);
            memcpy(&temp_buffer[chunk_start_offset], current_data, chunk_size);
            flash_page_erase(page_start);
            flash_byte_write(page_start, temp_buffer, PAGE_SIZE);

            xf_free(temp_buffer);
        } else {
            flash_byte_write(current_offset, (uint8_t *)current_data, chunk_size);
        }

        current_offset  += chunk_size;
        current_data    += chunk_size;
        remaining       -= chunk_size;
    }
    return XF_OK;
}

static xf_err_t safe_flash_byte_read(uint32_t offset, uint8_t *buff, uint32_t blen)
{
    if (blen % 4 != 0) {
        XF_LOGE(TAG, "Read length (blen) must be a multiple of 4.");
        XF_LOGE(TAG, "Read length provided: %d", (int)blen);
        return XF_FAIL;
    }
    flash_byte_read(offset, buff, blen);
    return XF_OK;
}

#endif /* CONFIG_PORT_FAL_ENABLE */
